var validationMethods = {
	required: function(value){ return !!value; },

	remote: function(value, param){
		var name = this.attr("name"),
			url;

		param = typeof param === "string" ? param = {url: param} : $.extend({}, param);
		
		if(!param.data && name){
			param.data = {};
			param.data[name] = value;
		}

		return $.ajax(param);
	}
},
	
validationMessages = {
	required: "This field is required.",
	remote: "Please fix this field.",
	email: "Please enter a valid email address.",
	url: "Please enter a valid URL.",
	date: "Please enter a valid date.",
	dateISO: "Please enter a valid date (ISO).",
	number: "Please enter a valid number.",
	digits: "Please enter only digits.",
	creditcard: "Please enter a valid credit card number.",
	equalTo: "Please enter the same value again.",
	accept: "Please enter a value with a valid extension.",
	maxlength: "Please enter no more than {0} characters.",
	minlength: "Please enter at least {0} characters.",
	rangelength: "Please enter a value between {0} and {1} characters long.",
	range: "Please enter a value between {0} and {1}.",
	max: "Please enter a value less than or equal to {0}.",
	min: "Please enter a value greater than or equal to {0}."
};

function getRulesFrom(element){
	var rules = $.data(element[0], "rules.pencil") || [];
	
	$.each(validationMethods, function(name, method){
		var param = element.attr(name) || element.attr("data-" + name),
			message;

		if(param !== undefined){
			message = validationMessages[name];
			rules.push({
				name: name,
				method: method,
				param: param
			});
		}
	});

	return rules;
}

$.addValidationRule = function(name, rule, validationMessage){
	if(rule === undefined){
		throw "Not enough arguments";
	}
};



$.extend($.fn, {
	
	addRule: function(method, message){
		var rules = $.data(this[0], "rules.pencil") || [];
		rules.push({method: method, validationMessage: message});
		$.data(this[0], "rules.pencil", rules);

		return this;
	},

	checkValidity: function(){
		var input = this,
			rules = getRulesFrom(input),
			pendings = [],
			pendingLength = 0,
			validationMessage,
			valid = true,
			resolved,
			triggerHandler;

		triggerHandler = function(){
			input.trigger("aftercheckvalidity", [valid, validationMessage]);
		};

		$.each(rules, function(_, rule){
			var method = rule.method,
				ret;
			
			if(!$.isFunction(method)){
				throw "rule-method is undefined";
			}
			
			ret = method.call(input, input.val(), rule.param);

			if(typeof ret !== "boolean"){
				pendings.push({xhr: ret, rule: rule});
			}else if(ret === false){
				valid = false;
				validationMessage = rule.validationMessage;
			}
			
			return valid;
		});

		pendingLength = pendings.length;
		if(valid && pendingLength){
			valid = undefined;
			$.each(pendings, function(_, pending){
				pending.xhr.success(function(response){
					if(!resolved){
						pendingLength--;

						if(response === false){
							resolved = true;
							valid = false;
							validationMessage = pending.rule.validationMessage;
							triggerHandler();
						
						}else if(pendingLength === 0){
							resolved = true;
							valid = true;
							validationMessage = undefined;
							triggerHandler();
						}
					}
				});
			});
		}

		if(valid !== undefined){
			triggerHandler();
		}

		return valid;
	}
	
});
